package top.eati.linguisticsdept.dict

import apijson.Log
import apijson.StringUtil
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.ApplicationContextAware
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import kotlin.Throws
import kotlin.jvm.JvmStatic
import org.springframework.boot.SpringApplication
import apijson.framework.APIJSONParser
import apijson.framework.APIJSONApplication
import apijson.framework.APIJSONCreator
import apijson.orm.AbstractVerifier
import apijson.orm.SQLConfig
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.alibaba.fastjson.serializer.PropertyFilter
import com.alibaba.fastjson.serializer.SerializeConfig
import org.postgresql.jdbc.PgArray
import unitauto.jar.UnitAutoApp
import unitauto.MethodUtil.InstanceGetter
import unitauto.MethodUtil.JSONCallback
import org.springframework.beans.BeansException
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import org.postgresql.jdbc.PgResultSet
import top.eati.linguisticsdept.dict.fastjson.PGArrayFilter
import top.eati.linguisticsdept.dict.springmvc.MainCtrller
import unitauto.MethodUtil
import java.lang.Exception
import java.lang.reflect.Modifier
import javax.naming.Context

/**SpringBootApplication
 * 右键这个类 > Run As > Java Application
 * @author Lemon
 */
@Configuration
@SpringBootApplication
class EatiLinguisticsDepartmentToolsDictionaryApp : ApplicationContextAware{
    companion object {
        private const val TAG = "DemoApplication"
        @Throws(Exception::class)
        @JvmStatic
        fun main(args: Array<String>) {
            SerializeConfig.globalInstance.addFilter(PgArray::class.java, PGArrayFilter())
            SpringApplication.run(EatiLinguisticsDepartmentToolsDictionaryApp::class.java, *args)

            // FIXME 不要开放给项目组后端之外的任何人使用 UnitAuto（强制登录鉴权）！！！如果不需要单元测试则移除相关代码或 unitauto.Log.DEBUG = false;
            // 上线生产环境前改为 false，可不输出 APIJSONORM 的日志 以及 SQLException 的原始(敏感)信息
            Log.DEBUG = true
            unitauto.Log.DEBUG = Log.DEBUG
            APIJSONApplication.init(false)
        }

        // 全局 ApplicationContext 实例，方便 getBean 拿到 Spring/SpringBoot 注入的类实例
        var applicationContext: ApplicationContext? = null
            private set

        init {
            // APIJSON 配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

            // 使用本项目的自定义处理类
            APIJSONApplication.DEFAULT_APIJSON_CREATOR = object: APIJSONCreator() {
                override fun createSQLConfig(): SQLConfig {
                    return top.eati.linguisticsdept.dict.apijson.SQLConfig()

                }
            };

            // APIJSON 配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            var d = "";

            // UnitAuto 单元测试配置  https://github.com/TommyLemon/UnitAuto  <<<<<<<<<<<<<<<<<<<<<<<<<<<
            // FIXME 不要开放给项目组后端之外的任何人使用 UnitAuto（强制登录鉴权）！！！如果不需要单元测试则移除相关代码或 unitauto.Log.DEBUG = false;
            UnitAutoApp.init()

            // 适配 Spring 注入的类及 Context 等环境相关的类
            val ig = MethodUtil.INSTANCE_GETTER
            MethodUtil.INSTANCE_GETTER = InstanceGetter { clazz, classArgs, reuse ->
                if (applicationContext != null && ApplicationContext::class.java.isAssignableFrom(clazz) && clazz.isAssignableFrom(
                        applicationContext!!.javaClass
                    )
                ) {
                    return@InstanceGetter applicationContext!!
                }
                if (reuse != null && reuse && (classArgs == null || classArgs.isEmpty())) {
                    applicationContext!!.getBean(clazz)
                } else ig.getInstance(clazz, classArgs, reuse)
            }

            // 排除转换 JSON 异常的类，一般是 Context 等环境相关的类
            val jc = MethodUtil.JSON_CALLBACK
            MethodUtil.JSON_CALLBACK = object : JSONCallback {
                override fun newSuccessResult(): JSONObject {
                    return jc.newSuccessResult()
                }

                override fun newErrorResult(e: Throwable): JSONObject {
                    return jc.newErrorResult(e)
                }

                override fun parseJSON(type: String, value: Any): JSONObject {
                    var value: Any? = value
                    if (value == null || unitauto.JSON.isBooleanOrNumberOrString(value) || value is JSON || value is Enum<*>) {
                        return jc.parseJSON(type, value)
                    }
                    if (value is ApplicationContext
                        || value is Context
                        || value is org.apache.catalina.Context
                        || value is ch.qos.logback.core.Context
                    ) {
                        value = value.toString()
                    } else {
                        try {
                            value = JSON.parse(JSON.toJSONString(value, PropertyFilter { obj, name, value ->
                                if (value == null) {
                                    return@PropertyFilter true
                                }
                                if (value is ApplicationContext
                                    || value is Context
                                    || value is org.apache.catalina.Context
                                    || value is ch.qos.logback.core.Context
                                ) {
                                    return@PropertyFilter false
                                }

                                // 防止通过 UnitAuto 远程执行 getDBPassword 等方法来查到敏感信息，但如果直接调用 public String getDBUri 这里没法拦截，仍然会返回敏感信息
                                if (obj is SQLConfig) {
                                    // 这个类部分方法不序列化返回
                                    if ("dBUri".equals(name, true) || "dBPassword".equals(name, true) || "dBAccount".equals(name, true)) {
                                        return@PropertyFilter false
                                    }
                                    return@PropertyFilter false  // 这个类所有方法都不序列化返回
                                }

                                // 所有类中的方法只要包含关键词就不序列化返回
                                val n = StringUtil.toLowerCase(name)
                                if (n.contains("database") || n.contains("schema") || n.contains("dburi") || n.contains(
                                        "password"
                                    ) || n.contains("account")
                                ) {
                                    false
                                } else Modifier.isPublic(value.javaClass.modifiers)
                            }))
                        } catch (e: Exception) {
                            Log.e(TAG, "toJSONString  catch ${e.message}")
                        }
                    }
                    return jc.parseJSON(type, value)
                }
            }

            // UnitAuto 单元测试配置  https://github.com/TommyLemon/UnitAuto  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


            // 把以下需要用到的数据库驱动取消注释即可，如果这里没有可以自己新增
            //		try { //加载驱动程序
            //			Log.d(TAG, "尝试加载 SQLServer 驱动 <<<<<<<<<<<<<<<<<<<<< ");
            //			Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
            //			Log.d(TAG, "成功加载 SQLServer 驱动！>>>>>>>>>>>>>>>>>>>>> ");
            //		}
            //		catch (ClassNotFoundException e) {
            //			e.printStackTrace();
            //			Log.e(TAG, "加载 SQLServer 驱动失败，请检查 pom.xml 中 net.sourceforge.jtds 版本是否存在以及可用 ！！！");
            //		}
            //
            //		try { //加载驱动程序
            //			Log.d(TAG, "尝试加载 Oracle 驱动 <<<<<<<<<<<<<<<<<<<<< ");
            //			Class.forName("oracle.jdbc.driver.OracleDriver");
            //			Log.d(TAG, "成功加载 Oracle 驱动！>>>>>>>>>>>>>>>>>>>>> ");
            //		}
            //		catch (ClassNotFoundException e) {
            //			e.printStackTrace();
            //			Log.e(TAG, "加载 Oracle 驱动失败，请检查 pom.xml 中 com.oracle.jdbc 版本是否存在以及可用 ！！！");
            //		}
            //
            //		try { //加载驱动程序
            //			Log.d(TAG, "尝试加载 DB2 驱动 <<<<<<<<<<<<<<<<<<<<< ");
            //			Class.forName("com.ibm.db2.jcc.DB2Driver");
            //			Log.d(TAG, "成功加载 DB2 驱动！>>>>>>>>>>>>>>>>>>>>> ");
            //		}
            //		catch (ClassNotFoundException e) {
            //			e.printStackTrace();
            //			Log.e(TAG, "加载 DB2 驱动失败，请检查 pom.xml 中 com.ibm.db2 版本是否存在以及可用 ！！！");
            //		}
        }
    }


    @Throws(BeansException::class)
    override fun setApplicationContext(applicationContext: ApplicationContext) {
        Companion.applicationContext = applicationContext
    }

    // 支持 APIAuto 中 JavaScript 代码跨域请求 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    @Bean
    fun corsConfigurer(): WebMvcConfigurer {
        return object : WebMvcConfigurer {
            override fun addCorsMappings(registry: CorsRegistry) {
                registry.addMapping("/**")
                    .allowedOriginPatterns("*")
                    .allowedMethods("*")
                    .allowCredentials(true)
                    .exposedHeaders(MainCtrller.APIJSON_DELEGATE_ID) // Cookie 和 Set-Cookie 怎么设置都没用 ,Cookie,Set-Cookie")   // .exposedHeaders("*")
                    .maxAge(3600)
            }
        }
    } // 支持 APIAuto 中 JavaScript 代码跨域请求 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}